package com.derbysoft.synxisadapter.dswitch2.service.impl;

import com.derbysoft.remote.dswitch.core.*;
import com.derbysoft.remote.dswitch.generic.constants.SmokingType;
import com.derbysoft.remote.dswitch.generic.constants.UniqueIDType;
import com.derbysoft.remote.dswitch.generic.dto.UniqueIDDTO;
import com.derbysoft.remote.dswitch.hotel.dto.*;
import com.derbysoft.synxisadapter.commons.dto.synxis.OTAHotelResRQ;
import com.derbysoft.synxisadapter.commons.dto.synxis.OTAHotelResRS;
import com.derbysoft.synxisadapter.commons.exception.ChannelNotFoundException;
import com.derbysoft.synxisadapter.commons.model.Channel;
import com.derbysoft.synxisadapter.commons.model.Hotel;
import com.derbysoft.synxisadapter.commons.model.Reservation;
import com.derbysoft.synxisadapter.commons.repository.ChannelRepository;
import com.derbysoft.synxisadapter.commons.repository.HotelRepository;
import com.derbysoft.synxisadapter.commons.repository.ReservationRepository;
import com.derbysoft.synxisadapter.dswitch2.service.HotelProviderService;
import com.derbysoft.synxisadapter.dswitch2.translator.impl.TranslateHelper;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by: jason
 * Date: 2012-05-29
 */
public abstract class AbstractReservationService extends
        AbstractHotelService<GenericRQ<HotelReservationRQ>, GenericRS<HotelReservationRS>, OTAHotelResRQ, OTAHotelResRS> {

    protected ReservationRepository reservationRepository;

    protected HotelProviderService<GenericRQ<HotelAvailRQ>, GenericRS<HotelAvailRS>> hotelAvailService;

    protected HotelRepository hotelRepository;

    protected ChannelRepository channelRepository;

    protected Channel findChannel(UniqueIDDTO uniqueIDDTO) {
        Channel channel = channelRepository.findByPassport(uniqueIDDTO.getId());

        if (channel == null) {
            throw new ChannelNotFoundException(uniqueIDDTO.getId());
        }
        return channel;
    }

    protected GenericRS<HotelReservationRS> availErrorRS(GenericRQ<HotelReservationRQ> rq, GenericRS<HotelAvailRS> availRS) {
        GenericRS<HotelReservationRS> rs = new GenericRS<HotelReservationRS>();
        rs.setErrors(availRS.getErrors());
        rs.setHeader(new ResponseHeader(rq.getHeader().getTaskId()));
        return rs;
    }

    protected void updateStatus(String ersp, Reservation.Status status) {
        if (ersp == null) {
            return;
        }
        reservationRepository.updateStatus(ersp, status);
    }

    protected String saveReservation(HotelReservationRQ hotelReservationRQ) {
        Hotel hotel = hotelRepository.findByPassport(
                hotelReservationRQ.getHotelReservation().getHotelRef().getCode(), Hotel.Status.ENABLED);

        String ersp = TranslateHelper.find(hotelReservationRQ.getHotelReservationIDs(), UniqueIDType.ERSP);

        reservationRepository.create(ersp, hotel.getId(), hotel.getPassport());

        return ersp;
    }

    protected RoomRateDTO findRoomRate(HotelAvailRS hotelAvailRS, HotelReservationDTO reservation) {
        AvailRoomStayDTO availRoomStayDTO = findAvailRoomStayDTO(hotelAvailRS, reservation);
        if (availRoomStayDTO != null) {
            return availRoomStayDTO.getRoomRate();
        }
        return null;
    }

    protected CancelPolicyDTO findCancelPolicy(HotelAvailRS hotelAvailRS, HotelReservationDTO reservation) {
        AvailRoomStayDTO availRoomStayDTO = findAvailRoomStayDTO(hotelAvailRS, reservation);
        if (availRoomStayDTO != null) {
            return availRoomStayDTO.getRatePlan().getCancelPolicy();
        }
        return null;
    }

    protected AvailRoomStayDTO findAvailRoomStayDTO(HotelAvailRS hotelAvailRS, HotelReservationDTO reservation) {
        String ratePlanCode = reservation.getRoomStays().get(0).getRatePlanCode();
        String roomTypeCode = reservation.getRoomStays().get(0).getRoomTypeCode();
        for (AvailRoomStayDTO availRoomStayDTO : hotelAvailRS.getHotelAvailRoomStay().getRoomStays()) {
            if (availRoomStayDTO.getRatePlan().getCode().equals(ratePlanCode) && availRoomStayDTO.getRoomType().getCode().equals(roomTypeCode)) {
                return availRoomStayDTO;
            }
        }
        return null;
    }

    protected List<ChargeItemDTO> createTaxes(List<AvailRoomStayDTO> roomStays, GenericRQ<HotelReservationRQ> rq) {
        String ratePlanCode = rq.getPayload().getHotelReservation().getRoomStays().get(0).getRatePlanCode();
        String roomTypeCode = rq.getPayload().getHotelReservation().getRoomStays().get(0).getRoomTypeCode();
        for (AvailRoomStayDTO roomStay : roomStays) {
            if (roomStay.getRatePlan().getCode().equals(ratePlanCode) && roomStay.getRoomType().getCode().equals(roomTypeCode)) {
                return filterZeroAmount(roomStay.getRatePlan().getTaxes());
            }
        }
        return new ArrayList<ChargeItemDTO>();
    }

    private List<ChargeItemDTO> filterZeroAmount(List<ChargeItemDTO> taxes) {
        List<ChargeItemDTO> chargeItemDTOs = new ArrayList<ChargeItemDTO>();
        for (ChargeItemDTO tax : taxes) {
            if (tax.getValue().compareTo(BigDecimal.ZERO) != 0) {
                chargeItemDTOs.add(tax);
            }
        }
        return chargeItemDTOs;
    }

    protected GenericRS<HotelAvailRS> getAvailRS(GenericRQ<HotelReservationRQ> rq) {
        HotelReservationDTO hotelReservation = rq.getPayload().getHotelReservation();
        ReservationRoomStayDTO roomStayDTO = hotelReservation.getRoomStays().get(0);
        GenericRQ<HotelAvailRQ> availRQ = new GenericRQ<HotelAvailRQ>();
        availRQ.setHeader(rq.getHeader());
        HotelAvailRQ hotelAvailRQ = new HotelAvailRQ();
        availRQ.setPayload(hotelAvailRQ);
        HotelAvailCriteriaDTO criteria = new HotelAvailCriteriaDTO();
        criteria.setHotelRef(hotelReservation.getHotelRef());
        criteria.setLanguageType(hotelReservation.getLanguageType());
        criteria.setStayDateRange(roomStayDTO.getStayDateRange());
        RoomStayCandidateDTO candidate = createRoomStayCandidate(roomStayDTO);
        criteria.setRoomStayCandidates(candidate);
        hotelAvailRQ.setAvailCriteria(criteria);
        return hotelAvailService.invoke(availRQ);
    }

    protected RoomStayCandidateDTO createRoomStayCandidate(ReservationRoomStayDTO roomStayDTO) {
        RoomStayCandidateDTO candidate = new RoomStayCandidateDTO();
        candidate.setSmoking(SmokingType.INDIFFERENT);
        candidate.setGuestCounts(roomStayDTO.getGuestCounts());
        candidate.setNumberOfUnits(roomStayDTO.getNumberOfUnits());
        return candidate;
    }

    protected GenericRS<HotelReservationRS> noAllotmentRS(GenericRQ<HotelReservationRQ> rq) {
        ReservationRoomStayDTO roomStayDTO = rq.getPayload().getHotelReservation().getRoomStays().get(0);
        GenericRS<HotelReservationRS> rs = new GenericRS<HotelReservationRS>();
        ErrorsDTO errors = new ErrorsDTO();
        String errorMessage = String.format("No allotment for ratePlanCode[%s] roomTypeCode[%s] numberOfUnits[%d]",
                roomStayDTO.getRatePlanCode(), roomStayDTO.getRoomTypeCode(), roomStayDTO.getNumberOfUnits());

        errors.getErrorList().add(new ErrorDTO("NoAllotment", errorMessage, SOURCE));
        rs.setErrors(errors);
        rs.setHeader(new ResponseHeader(rq.getHeader().getTaskId()));
        return rs;
    }

    public void setReservationRepository(ReservationRepository reservationRepository) {
        this.reservationRepository = reservationRepository;
    }

    public void setHotelAvailService(HotelProviderService<GenericRQ<HotelAvailRQ>, GenericRS<HotelAvailRS>> hotelAvailService) {
        this.hotelAvailService = hotelAvailService;
    }

    public void setHotelRepository(HotelRepository hotelRepository) {
        this.hotelRepository = hotelRepository;
    }

    public void setChannelRepository(ChannelRepository channelRepository) {
        this.channelRepository = channelRepository;
    }
}
